package xyz.eden.gateway.springcloud.admin.protocol.iplist;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class IpReactorServiceInstanceLoadBalancer extends RoundRobinLoadBalancer {

    private IpClientServiceInstanceSourceConfig serviceInstanceSourceConfig;
    private final AtomicInteger position;

    public IpReactorServiceInstanceLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
                                                String serviceId,
                                                IpClientServiceInstanceSourceConfig serviceInstanceSourceConfig) {
        super(serviceInstanceListSupplierProvider, serviceId);
        this.serviceInstanceSourceConfig = serviceInstanceSourceConfig;
        this.position = new AtomicInteger(new Random().nextInt(1000));
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        if (!(request instanceof IpRequest)) {
            Mono<Response<ServiceInstance>> responseMono = super.choose(request);
            return responseMono;
        }
        IpRequest ipRequest = (IpRequest) request;
        List<IpClientServiceInstance> serviceInstance = serviceInstanceSourceConfig.getServiceInstanceList(ipRequest.getServerId());
        Response<ServiceInstance> response = getInstanceResponse(serviceInstance);
        Response<ServiceInstance> chooseResponse = Objects.isNull(response) ? new EmptyResponse() : response;
        return Mono.just(chooseResponse);
    }


    private Response<ServiceInstance> getInstanceResponse(List<IpClientServiceInstance> instances) {
        if (instances.isEmpty()) {
            log.warn("No servers available for service: ");
            return new EmptyResponse();
        }
        // TODO: enforce order?
        int pos = Math.abs(this.position.incrementAndGet());

        ServiceInstance instance = instances.get(pos % instances.size());

        return new DefaultResponse(instance);
    }
}
